#include "multifile.h"
#include "hw_translate.h"
#include "raw_connection.h"
#include "packet.h"
#include "../packet_headers/etherhdr.h"
#include "../packet_headers/iphdr.h"
#include "../packet_headers/tcphdr.h"
#include "../packet_headers/udphdr.h"
#include "../packet_headers/icmphdr.h"
#include "../packet_headers/arphdr.h"
#include "../packet_headers/flags.h"
#include "../Errors/errors.h"
#include <arpa/inet.h>
#include <fstream>
#include <list>
#include <algorithm>
using namespace std;

class packet_type_data {
public:
	unsigned short id;
	unsigned short round;
};

class packet_type {
public:
	char * packet;
	packet_type_data * data_pointer;
	int len;
	int type;
};

void execute_file(char * _filename) {
	ifstream infile(_filename, ifstream::in);
	if(!infile) {
		printf("Invalid filename\n");
		return;
	}

	int n, nbursts, npackettypes, delta;
	infile >> n;
	infile >> nbursts;
	infile >> npackettypes;
	infile >> delta;

	// Get each packet type
	std::list<packet_type> packet_types;
	char data[900];
	for(int i = 0; i < npackettypes; i++) {
		packet_type next;
		string proto;
		infile >> proto;
		unsigned char precedence = 0;
		bool d=false, t=false, r=false;
		unsigned short id = 0, corrupt = 0;
		if(proto == "tcp") {
			infile >> precedence >> d >> t >> r >> id;
			// Get TCP info
			string srcaddr, destaddr;
			unsigned short srcport, destport;
			unsigned int seq, ack;
			string flags;
			unsigned char tcpflags;
			infile >> srcaddr >> destaddr >> srcport
						>>destport >> flags >> seq >> ack >> corrupt;
			if(flags == "ack")
				tcpflags = TCP_Flags(true,false,false,false,false,false);
			else if(flags == "urgent")
				tcpflags = TCP_Flags(false,true,false,false,false,false);
			else if(flags == "push")
				tcpflags = TCP_Flags(false,false,true,false,false,false);
			else if(flags == "reset")
				tcpflags = TCP_Flags(false,false,false,true,false,false);
			else if(flags == "syn")
				tcpflags = TCP_Flags(false,false,false,false,true,false);
			else if(flags == "fin")
				tcpflags = TCP_Flags(false,false,false,false,false,true);
			next.packet = (char*)tcp_packet(srcaddr.c_str(), destaddr.c_str(),
								(byte)IP_TOS_FILL(precedence,d,t,r), id,
								data, 900, srcport, destport, seq,
								ack, (byte)tcpflags, corrupt);
			next.len = sizeof(struct etherheader) + sizeof(struct ipheader)
							+ sizeof(struct tcpheader) + 900;
			next.type = htons(i+1);
			next.data_pointer = (packet_type_data*)(next.packet + next.len - 900);
			next.data_pointer->id = htons(i+1);
			next.data_pointer->round = 0;
			packet_types.push_back(next);
		}
		else if(proto == "udp") {
			infile >> precedence >> d >> t >> r >> id;
			string srcaddr, destaddr;
			unsigned short srcport, destport;
			infile >> srcaddr >> destaddr >> srcport
									>>destport >> corrupt;
			next.packet = (char*)udp_packet(srcaddr.c_str(), destaddr.c_str(),
					(byte)IP_TOS_FILL(precedence,d,t,r), id,
					data, 900, srcport, destport, corrupt);
			next.len = sizeof(struct etherheader) + sizeof(struct ipheader)
										+ sizeof(struct udpheader) + 900;
			next.type = i+1;
			next.data_pointer = (packet_type_data*)(next.packet + next.len - 900);
			next.data_pointer->id = htons(i+1);
			next.data_pointer->round = 0;
			packet_types.push_back(next);
		}
		else if(proto == "icmp") {
			infile >> precedence >> d >> t >> r >> id;
			string srcaddr, destaddr;
			unsigned char type, code;
			unsigned short icmpid, icmpseq;
			infile >> srcaddr >> destaddr >> type >> code
						>> icmpid >> icmpseq >> corrupt;
			next.packet = (char*)icmp_packet(srcaddr.c_str(), destaddr.c_str(),
					(byte)IP_TOS_FILL(precedence,d,t,r),
					id, type, code, icmpid, icmpseq, corrupt);
			next.len = sizeof(struct etherheader) + sizeof(struct ipheader)
										+ sizeof(struct icmpheader);
			next.type = i+1;
			next.data_pointer = 0;
			packet_types.push_back(next);
		}
		else if(proto == "arp") {
			string srcaddr, desthw, destaddr, op;
			infile >> srcaddr >> desthw >> destaddr >> op;
			unsigned short operation = 1;
			if(op == "request")
				operation = 1;
			else
				operation = 2;
			char * dhw = get_hw_addr(desthw.c_str());
			arp_packet(operation, srcaddr.c_str(), dhw, destaddr.c_str());
			next.type = i+1;
			next.data_pointer = 0;
			packet_types.push_back(next);
		}
	}

	RawConnection rc(get_interface());
	if(rc.state != 0) {
		printf("Error: %s\n", get_error_message());
		return;
	}

	// Start sending in bursts
	for(int i = 0; i < nbursts; i++) {
		printf("\nRound %d\n",i+1);

		for(int j = 0; j < n; j++) {
			//std::random_shuffle(packet_types.begin(), packet_types.end());

			for(list<packet_type>::iterator iter = packet_types.begin();
				iter != packet_types.end(); iter++) {
				if(iter->data_pointer != 0)
					iter->data_pointer->round = htons(i+1);
				int result = rc.send(iter->packet, iter->len);
			}
		}

		//Wait for delta
		usleep(delta*1000);
	}

	printf("\nSimulation complete.\n");
}
